iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 18
0
自我挑戰組

菜雞們,讓我們一起征服JS及React吧系列 第 18

React菜雞-Day18:實戰!寫個To Do List - part3 優化使用者體驗

  • 分享至 

  • xImage
  •  
tags: 鐵人賽 React javascript nodejs


/images/emoticon/emoticon12.gif
鐵人賽第18天,今天我們要來優化我們的ToDoApp,讓它更好用,提升它的好用度。

還可以優化哪些... /images/emoticon/emoticon13.gif

  • 每個event能夠有 Remove button
  • 按下send後,消除輸入的文字
  • 重新整理能夠保存toDoList資料

加入代辦事項移除功能

  • 為了讓每個待辦事項,都有一個對應的Delete按鈕,我們新增一個Message.js,讓每個獨立的代辦事項,有自己的元件。
  • 為了讓顯示有bullet前置符號,我們將原本的<div>改為<li>
// Message.js
import React from "react";

export default function Message(props) {
  return (
    <li>
      {props.msg} <button>{"Delete"}</button>
    </li>
  );
}
  • 內容使用<li>來顯示,當然也要針對ToDoList.js中的<div>改為<ul>
// ToDoList.js
import React, { useContext } from "react";
import { ContextStore } from "./ToDoApp";
import Message from "./Message";  //<-- 引入Message

export default function ToDoList() {
  const { appReducer } = useContext(ContextStore);
  const toDoList = appReducer[0];
  const result = toDoList.map((e, idx) => (
    <Message key={`cmd-${idx}`} msg={e} />   //<-- 改用Message component來顯示
  ));

  return (<ul>{result}</ul>)    //<--改為<ul>去顯示
}

  • 測試一下,不錯喔,每個代辦事項都有bullet符號,旁邊還有Remove提供訊息的刪除

新增Delete功能到Reducer中

// reducer.js
export default function reducers(toDoList, action) {
  let newToDoList = [...toDoList];
  switch (action.type) {
    case "ADD":
      newToDoList.push(action.newEvent); 
      break;
    
    // 新增Delete的部分
    case "DEL":
      newToDoList = newToDoList.filter(msg=>msg!==action.delMsg)  // 利用filter,移除toDoList之中的delMsg
      // console.log("Remove: "+action.delMsg); //<--利用console.log(), 可以快速測試是否與事件觸發連結上
      break;

    default:
      console.log("toDoList", toDoList);
      break;
  }

  return newToDoList;
}

啟動Delete按鈕的onClick事件

  • 回到Message.js,我們將delete buttononClick事件與ReducerDEL做個連結,讓訊息可以順利的從Array中移除
// Message.js
import React, {useContext} from "react";
import { ContextStore } from "./ToDoApp";

// 新增onClick delete事件,並與`Reducer`的`DEL`做個連結
function handleDelEvent(delMsg, dispatch) {
  return (e) => dispatch({ type: "DEL", delMsg: delMsg });
}

export default function Message(props) {
  const {appReducer} = useContext(ContextStore);
  const dispatch = appReducer[1];
  return (
    <li>
      {props.msg} <button onClick={handleDelEvent(props.msg, dispatch)}>{"Delete"}</button>
    </li>
  );
}
  • 按下刪除前

  • 按下刪除之後,順利的刪除囉

按下send按鈕,清除輸入框的文字

  • 我們在新增代辦事項後,要新增下一個時,還得自己手動清除,總是不太任性,來優化一下。
  • 開啟 input.js
  • 第一步:還記得input中的onChane會存取msg 吧,而input value剛好也是存取input框中的文字,讓這個變數跟著msg就可以實現囉
  • 第二步:我們傳遞setMsghandleAddClick func,
// input.js
import React, { useContext, useState } from "react";
import { ContextStore } from "./ToDoApp";

function handleAddClick(newMsg, dispatch, setMsg) {
  const anEvent = { type: "ADD", newEvent: newMsg };
  return (e) => {
    setMsg("");      //<-- 給空的字串,完成清除input的動作
    dispatch(anEvent);
  };
}

export default function Input() {
  const [msg, setMsg] = useState("");
  const { appReducer } = useContext(ContextStore);
  const dispatch = appReducer[1];

  return (
    <div>
      <input
        style={{ width: "150px" }}
        onChange={(e) => setMsg(e.target.value)}
        value={msg}
    ></input>
      <button onClick={handleAddClick(msg, dispatch, setMsg)}>{"Send"}</button>
    </div>
  );
}

重新整理網頁,讓代辦事項還存在 /images/emoticon/emoticon47.gif

  • 開發過程中,你可能已經發現,每次重新整理,輸入的代辦事項也會跟著消失。
  • 因此,我們可以善用Chrome browser內建的localStore來暫存toDoList,並在每次存取時更新它,就不用擔心重新整理後,代辦事項消失囉。
// ToDoApp.js
import React, { useReducer, createContext } from "react";
import reducers from "./reducers";
import ToDoList from "./ToDoList";
import Input from "./Input";

export const ContextStore = createContext();

export default function ToDoApp() {
  const appReducer = useReducer(
    reducers,
    localStorage.getItem("toDoList")
      ? localStorage.getItem("toDoList").split(",") //<-- true: 對字串做split, 可直接得到array, ex: 'aaa,bbb,ccc'=>['aaa', 'bbb', 'ccc']
      : [] //<-- false: 給一個空的陣列即可
  ); 

  return (
    <ContextStore.Provider value={{ appReducer: appReducer }}>
      <div>
        <h2 style={{ borderBottom: "1.5px black solid" }}>To Do List</h2>
        <Input />
        <ToDoList />
      </div>
    </ContextStore.Provider>
  );
}
  • reducers.js中,隨時更新toDoListlocalStorage
// reducers
export default function reducers(toDoList, action) {
  let newToDoList = [...toDoList];
  switch (action.type) {
    case "ADD":
      newToDoList.push(action.newEvent); 
      break;
    
    // 新增Delete的部分
    case "DEL":
      newToDoList = newToDoList.filter(msg=>msg!==action.delMsg)  // 利用filter,移除toDoList之中的delMsg
      console.log("Remove: "+action.delMsg);
      break;

    default:
      console.log("toDoList", toDoList);
      break;
  }

  localStorage.setItem('toDoList', newToDoList) //<-- 讓browser能夠暫存這筆資料,重新整理就不用擔心資料遺失囉
  return newToDoList;
}

測試看看

  • 我們先開啟Google Chrome inspector的console看看,輸入localStorage.toDoList,不錯,確實有存進去喔!

Demo /images/emoticon/emoticon65.gif

  • 完成囉~撒花+歡呼

課後修改練習

  • 眼尖的朋友應該有發現,如果重複輸入一樣的代辦事項,按下刪除時,會把另一個一樣訊息的刪除。
  • 要解決它很簡單,賦予Message一個獨一無二的值,並在Reducer DEL進行判斷處理,這部分就交給各位練習看看!

結論

  • 今天我們學到一些小技巧,讓我們可以優化ToDoApp,提升app的使用者體驗。
  • 鐵人賽第18天完成,我們花了三天的時間,完成了一個app,過程中,我們將所學的複習運用,相信各位對React都更加熟練了。超棒的你~繼續Rock!!
    /images/emoticon/emoticon62.gif

上一篇
React菜雞-Day17:實戰!寫個To Do List - part2 - 讓app有血有肉有靈魂
下一篇
React菜雞-Day19:React第四招 - useEffect 讓你掌握Component的生命週期
系列文
菜雞們,讓我們一起征服JS及React吧30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言